/*  This file is part of CASpp.

    CASpp is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later versio

    CASpp is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with CASpp.  If not, see <http://www.gnu.org/licenses/>.

    Copyright 2014 Jérome LAURENS

    See https://code.google.com/p/idalgo/

*/

#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/log/trivial.hpp>
#include "node.h"

CASPP_NS_BEGIN

Node::BadRef::BadRef(): __super( "Bad reference request, see log file." ) {}

Node::Node()
    : __super()
    , line_m(0)
    , column_m(0) {
}

Node::Node(const Boolean::Values __x)
    : __super(BooleanRef(new Boolean(__x)))
    , line_m(0)
    , column_m(0) {
    ;
}

Node::Node(const std::string & __new_x, type_t type)
        : __super()
        , line_m(0)
        , column_m(0) {
    if (__new_x.length()>0) {
        switch(type) {
        case Type::kBOOL: *this = *makeBooleanNodeRef(__new_x); return;
        case Type::kLIST: *this = *makeListNodeRef(__new_x); return;
        case Type::kVECT: *this = *makeVectorNodeRef(__new_x); return;
        case Type::kSET:  *this = *makeSetNodeRef(__new_x); return;
        case Type::kMAP:  *this = *makeMapNodeRef(__new_x); return;
        case Type::kINT:  *this = *makeIntegerNodeRef(__new_x); return;
        case Type::kRAT:  *this = *makeRationalNodeRef(__new_x); return;
        case Type::kREAL: *this = *makeRealNodeRef(__new_x); return;
        case Type::kSYMB: *this = *makeSymbolNodeRef(__new_x); return;
        case Type::kVAR:  *this = *makeVariableNodeRef(__new_x); return;
        case Type::kFUNC: *this = *makeFunctionNodeRef(__new_x); return;
        case Type::kUNDEF:
        //case Type::kAUTO:
            *this = *makeBooleanNodeRef(__new_x);
            if (isBoolean()) return;
            *this = *makeIntegerNodeRef(__new_x);
            if (isInteger()) return;
            *this = *makeRationalNodeRef(__new_x);
            if (isRational()) return;
            *this = *makeRealNodeRef(__new_x);
            if (isReal()) return;
        case Type::kSTR: *this = *makeStringNodeRef(__new_x);
        default:
            return;
        }
    } else {
        *this = *makeStringNodeRef(__new_x);
    }
}

#ifdef CASPP_DO
#error Change macro name
#else
#define CASPP_DO(__k,__t,__T,__YPE)\
Node::Node(const __T##__YPE & __x): __super(boost::make_shared<__T##__YPE>(__x)), line_m(0), column_m(0) {};\
Node::Node(const __T##__YPE##Ref & __x): __super(__x), line_m(0), column_m(0) {};\
__T##__YPE##Ref & Node::__t##__YPE##Ref() {\
    try {\
        return boost::get<__T##__YPE##Ref>(*this);\
    }\
    catch (const boost::bad_get & e) { \
        BOOST_LOG_TRIVIAL(fatal) << "Code error: " << this->prettyType() << " node has no "<< CASPP_STRINGIFY(__T) << CASPP_STRINGIFY(__YPE) << "Ref.";\
        throw(BadRef());\
    } \
}\
const __T##__YPE##Ref & Node::__t##__YPE##Ref() const {\
    try {\
        return boost::get<__T##__YPE##Ref>(*this);\
    }\
    catch (const boost::bad_get & e) { \
    BOOST_LOG_TRIVIAL(fatal) << "Code error: " << this->prettyType() << " node has no "<< CASPP_STRINGIFY(__T) << CASPP_STRINGIFY(__YPE) << "Ref.";\
        throw(BadRef());\
    } \
} \
bool Node::is##__T##__YPE() const {\
    return boost::get<__T##__YPE##Ref>(this)!=0 && boost::get<__T##__YPE##Ref>(*this).get()!=0; \
}

    CASPP_DO(kBOOL,b,B,oolean)
    CASPP_DO(kSTR,s,S,tring)
    CASPP_DO(kLIST,l,L,ist)
    CASPP_DO(kVECT,v,V,ector)
    CASPP_DO(kSET,s,S,et)
    CASPP_DO(kMAP,m,M,ap)
    CASPP_DO(kINT,i,I,nteger)
    CASPP_DO(kRAT,r,R,ational)
    CASPP_DO(kREAL,r,R,eal)
    CASPP_DO(kSYMB,s,S,ymbol)
    CASPP_DO(kVAR,v,V,ariable)
    CASPP_DO(kFUNC,f,F,unction)

#undef CASPP_DO
#define CASPP_DO(__k,__t,__T,__YPE)\
NodeRef make##__T##__YPE##NodeRef(const std::string & s) {\
    __T##__YPE * p = 0;\
    try {\
        p = new __T##__YPE(s);\
    }\
    catch(...) {\
        ;\
    }\
    NodeRef result(new Node);\
    if (p) {\
        *result = __T##__YPE##Ref(p);\
    }\
    return result;\
}
    CASPP_DO(kBOOL,b,B,oolean)
    CASPP_DO(kSTR,s,S,tring)
//    CASPP_DO(kLIST,l,L,ist)
//    CASPP_DO(kVECT,v,V,ector)
//    CASPP_DO(kSET,s,S,et)
//    CASPP_DO(kMAP,m,M,ap)
    CASPP_DO(kINT,i,I,nteger)
    CASPP_DO(kRAT,r,R,ational)
    CASPP_DO(kREAL,r,R,eal)
    CASPP_DO(kSYMB,s,S,ymbol)
    CASPP_DO(kVAR,v,V,ariable)
    CASPP_DO(kFUNC,f,F,unction)

NodeRef makeListNodeRef(const std::string &) {
    NodeRef result(new NodeRef::element_type);
    * result = ListRef(new ListRef::element_type);
    return result;
}
NodeRef makeVectorNodeRef(const std::string &) {
    NodeRef result(new NodeRef::element_type);
    * result = VectorRef(new VectorRef::element_type);
    return result;
}
NodeRef makeSetNodeRef(const std::string &) {
    NodeRef result(new NodeRef::element_type);
    * result = SetRef(new SetRef::element_type);
    return result;
}
NodeRef makeListNodeRef(const NodeRef & nRa, const NodeRef &nRb) {
    NodeRef result = makeListNodeRef("");
    result->listRef()->push_back(nRa);
    result->listRef()->push_back(nRb);
    return result;
}
NodeRef makeVectorNodeRef(const NodeRef & nRa, const NodeRef &nRb) {
    NodeRef result = makeVectorNodeRef("");
    result->vectorRef()->push_back(nRa);
    result->vectorRef()->push_back(nRb);
    return result;
    ;
}
NodeRef makeSetNodeRef(const NodeRef & nRa, const NodeRef &nRb) {
    NodeRef result = makeSetNodeRef("");
    result->setRef()->push_back(nRa);
    result->setRef()->push_back(nRb);
    return result;
    ;
}

NodeRef makeMapNodeRef(const std::string &) {
    NodeRef result(new NodeRef::element_type);
    * result = MapRef(new Map);
    return result;
}
NodeRef makeFunctionNodeRef(const SymbolRef & symbol, const ListRef & arguments) {
    NodeRef result(new NodeRef::element_type);
    * result = MapRef(new Map);
    return result;
}

#if 0
    CASPP_DO(kDEC,d,D,ecimal)
    CASPP_DO(kCPLX,c,C,omplex)
    CASPP_DO(kROOT,r,R,oot)
    CASPP_DO(kSYMB,s,S,ymbol)
    CASPP_DO(kMOD,m,M,odulo)
    CASPP_DO(kUSER,u,U,ser)
    CASPP_DO(kMAP,m,M,ap)
    CASPP_DO(kEQW,E,E,QW)
    CASPP_DO(kGFX,G,G,FX)
    CASPP_DO(kPTR,p,P,ointer)
    CASPP_DO(kEXT,a,A,lgExt)
    CASPP_DO(kPOLY,p,P,olynomial)
    CASPP_DO(kSP1,s,S,parsePoly1)
#endif
#undef CASPP_DO
#endif

class _NodeTypeVisitor: public boost::static_visitor<Node::type_t>
{
public:
    result_type operator()(const BooleanRef & t) const {
      return t.get()? Type::kBOOL: Type::kUNDEF;
    }
    result_type operator()(const StringRef & t) const {
       return t.get()? Type::kSTR: Type::kUNDEF;
    }
    result_type operator()(const ListRef & t) const {
     return t.get()? Type::kLIST: Type::kUNDEF;
    }
    result_type operator()(const VectorRef & t) const {
     return t.get()? Type::kVECT: Type::kUNDEF;
    }
    result_type operator()(const SetRef & t) const {
     return t.get()? Type::kSET: Type::kUNDEF;
    }
    result_type operator()(const MapRef & t) const {
      return t.get()? Type::kMAP: Type::kUNDEF;
    }
    result_type operator()(const IntegerRef & t) const {
      return t.get()? Type::kINT: Type::kUNDEF;
    }
    result_type operator()(const RationalRef & t) const {
        return t.get()? Type::kRAT: Type::kUNDEF;
    }
    result_type operator()(const RealRef & t) const {
        return t.get()? Type::kREAL: Type::kUNDEF;
    }
    result_type operator()(const SymbolRef & t) const {
       return t.get()? Type::kSYMB: Type::kUNDEF;
    }
    result_type operator()(const VariableRef & t) const {
       return t.get()? Type::kVAR:  Type::kUNDEF;
    }
    result_type operator()(const FunctionRef & t) const {
        return t.get()? Type::kFUNC: Type::kUNDEF;
    }
};

Node::type_t Node::type() const {
    return boost::apply_visitor(_NodeTypeVisitor(),*this);
}
std::string Node::prettyType() const {
    return Type::name(type());
}

const std::string Node::description() const {
    return boost::apply_visitor(AgregateDescriptionVisitor(),*this);
}

bool Node::isMutable() const {
    switch(type()) {
        case Type::kSTR:
        case Type::kFUNC:
        case Type::kMAP:
        case Type::kLIST:
        case Type::kVECT:
        case Type::kSET:
        case Type::kPOLY:
            return true;
        default:return false;
    }
}

#warning deep copy nyi
#if 0
Node Node::copy () {
    if (isMutable()) {
        return *this;
    }
    Node result;// Null node

}
#endif

#if 0
class NodeCopyVisitor: public boost::static_visitor<Node>
{
public:
    result_type operator()(const IntegerRef & t) const {
        return Node(t);
    }
    result_type operator()(const RationalRef & t) const {
        return t.get()? Type::kRAT: Type::kUNDEF;
    }
    result_type operator()(const RealRef & t) const {
        return t.get()? Type::kREAL: Type::kUNDEF;
    }
    result_type operator()(const StringRef & t) const {
        return t.get()? Type::kSTR: Type::kUNDEF;
    }
    result_type operator()(const SymbolRef & t) const {
        return t.get()? Type::kSYMB: Type::kUNDEF;
    }
    result_type operator()(const VariableRef & t) const {
        return t.get()? Type::kVAR:  Type::kUNDEF;
    }
    result_type operator()(const FunctionRef & t) const {
        return t.get()? Type::kFUNC: Type::kUNDEF;
    }
};
#endif

Node::line_t Node::line() const {
    return line_m;
}
void Node::setLine(const line_t l) {
    line_m = l;
}
Node::column_t Node::column() const {
    return column_m;
}
void Node::setColumn(const column_t c) {
    column_m = c;
}
#if 0
class _push_backVisitor: public boost::static_visitor<bool>
{
public:
    result_type operator()(const BooleanRef & t, const NodeRef & n) const {
        return false;
    }
    result_type operator()(const StringRef & t, const NodeRef & n) const {
        return false;
    }
    result_type operator()(const ListRef & t, const NodeRef & n) const {
        if (t.get()) {
            t->push_back(n);
            return true;
        }
        return false;
    }
    result_type operator()(const VectorRef & t, const NodeRef & n) const {
        if (t.get()) {
            t->push_back(n);
            return true;
        }
        return false;
    }
    result_type operator()(const SetRef & t, const NodeRef & n) const {
        if (t.get()) {
            t->push_back(n);
            return true;
        }
        return false;
    }
    result_type operator()(const MapRef & t, const NodeRef & n) const {
        return false;
    }
    result_type operator()(const IntegerRef & t, const NodeRef & n) const {
        return false;
    }
    result_type operator()(const RationalRef & t, const NodeRef & n) const {
        return false;
    }
    result_type operator()(const RealRef & t, const NodeRef & n) const {
        return false;
    }
    result_type operator()(const SymbolRef & t, const NodeRef & n) const {
        return false;
    }
    result_type operator()(const VariableRef & t, const NodeRef & n) const {
        return false;
    }
    result_type operator()(const FunctionRef & t, const NodeRef & n) const {
        if (t.get()) {
            if (t->argumentsRef().get() == 0) {
                t->argumentsRef().reset(new FunctionRef::element_type::arguments_t::element_type);
            }
            t->argumentsRef()->push_back(n);
            return true;
        }
        return false;
    }
};

bool Node::push_back(const NodeRef & n) {
    return boost::apply_visitor(_push_backVisitor(),*this,n);
}
#endif

bool Node::push_back(const NodeRef & n) {
    if (isList()) {
        if (n->listRef().get() == 0) {
            n->listRef().reset(new ListRef::element_type);
        }
        n->listRef()->push_back(n);
        return true;
    } else if (isVector()) {
        if (n->vectorRef().get() == 0) {
            n->vectorRef().reset(new VectorRef::element_type);
        }
        n->vectorRef()->push_back(n);
        return true;
    } else if (isSet()) {
        if (n->setRef().get() == 0) {
            n->setRef().reset(new SetRef::element_type);
        }
        n->setRef()->push_back(n);
        return true;
    } else if (isFunction()) {
        if (n->functionRef().get() != 0) {
            typedef FunctionRef::element_type::arguments_t arguments_t;
            arguments_t args = n->functionRef()->argumentsRef();
            if (args.get() == 0) {
                args.reset(new arguments_t::element_type);
            }
            args->push_back(n);
            return true;
        }
        return false;
    } else {
        return false;
    }
}

CASPP_NS_END
